Maîtrisez l'API ResizeObserver pour suivre avec précision les changements de taille des éléments et créer des mises en page web robustes et responsives. Découvrez ses avantages, ses cas d'utilisation et les meilleures pratiques pour le développement web moderne.
L'API ResizeObserver : Suivi précis de la taille des éléments pour des mises en page dynamiques et responsives
Dans le vaste paysage en constante évolution du développement web, la création d'interfaces utilisateur véritablement responsives et adaptatives reste un défi majeur. Alors que les media queries ont longtemps servi de pierre angulaire pour adapter les mises en page à différentes tailles de viewport, le web moderne exige une approche plus granulaire : la responsivité au niveau du composant. C'est là que la puissante API ResizeObserver intervient, révolutionnant la manière dont les développeurs suivent et réagissent aux changements de taille d'un élément, indépendamment du viewport.
Ce guide complet explorera en profondeur l'API ResizeObserver, ses mécanismes, ses diverses applications, les meilleures pratiques, et comment elle permet aux développeurs de créer des expériences web hautement dynamiques et résilientes pour un public mondial.
Comprendre le problème fondamental : Pourquoi window.resize n'est pas à la hauteur
Pendant de nombreuses années, le principal mécanisme pour réagir aux changements de mise en page dans le navigateur était l'événement window.resize. Les développeurs attachaient des écouteurs d'événements à l'objet window pour détecter quand les dimensions du viewport du navigateur changeaient. Cependant, cette approche présente des limitations importantes dans le monde actuel axé sur les composants :
- Centré uniquement sur le viewport : L'événement
window.resizene se déclenche que lorsque la fenêtre du navigateur elle-même est redimensionnée. Il ne fournit aucune information sur les changements de taille des éléments individuels au sein du document dus à d'autres facteurs. - Portée limitée : Un composant peut avoir besoin d'ajuster sa mise en page interne si son conteneur parent rétrécit ou s'agrandit, même si la taille globale du viewport reste constante. Pensez à une barre latérale qui se réduit ou à un panneau d'onglets qui révèle un nouveau contenu.
window.resizen'offre aucune information sur ces changements localisés. - Sondage inefficace : Pour suivre les changements au niveau des éléments sans
ResizeObserver, les développeurs recouraient souvent à des mécanismes de sondage inefficaces et gourmands en performances utilisantsetInterval, vérifiant à plusieurs repriseselement.offsetWidthouelement.offsetHeight. Cela entraîne des calculs inutiles et des saccades potentielles. - Communication complexe entre composants : Orchestrer les changements de taille entre des composants profondément imbriqués ou indépendants devient un enchevêtrement complexe sans un moyen direct pour un composant de connaître son propre espace alloué.
Imaginez un scénario où un graphique de visualisation de données doit se redimensionner dynamiquement lorsque son élément <div> conteneur est ajusté par un utilisateur, peut-être via un séparateur déplaçable. window.resize serait inutile ici. C'est précisément le type de défi que ResizeObserver a été conçu pour résoudre.
Présentation de l'API ResizeObserver
L'API ResizeObserver offre un moyen performant et efficace d'observer les changements de taille de la boîte de contenu ou de la boîte de bordure d'un élément. Contrairement à window.resize, qui surveille le viewport, ResizeObserver se concentre sur les dimensions spécifiques d'un ou plusieurs éléments DOM cibles.
C'est un ajout puissant à la suite d'API web, permettant aux développeurs de :
- Réagir aux redimensionnements spécifiques des éléments : Être notifié chaque fois que la taille d'un élément observé change, que la fenêtre ait été redimensionnée ou non. Cela inclut les changements causés par les mises en page CSS (flexbox, grid), l'injection de contenu dynamique ou les interactions utilisateur.
- Éviter les boucles de redimensionnement infinies : L'API est conçue pour empêcher les boucles infinies qui pourraient se produire si un gestionnaire d'événement de redimensionnement modifiait directement la taille de l'élément observé, déclenchant un autre événement de redimensionnement. ResizeObserver regroupe les changements et les traite efficacement.
- Améliorer les performances : En fournissant un mécanisme déclaratif et événementiel, il élimine le besoin de sondages coûteux ou de hacks complexes avec IntersectionObserver pour le suivi de la taille.
- Permettre une véritable responsivité au niveau du composant : Les composants peuvent devenir réellement conscients de l'espace qui leur est alloué, ce qui conduit à des éléments d'interface utilisateur plus modulaires, réutilisables et robustes.
Comment fonctionne ResizeObserver : Une plongée pratique en profondeur
L'utilisation de l'API ResizeObserver implique quelques étapes simples : instancier un observateur, lui dire quels éléments surveiller, puis gérer les changements dans une fonction de rappel.
Instanciation et Observation
D'abord, vous créez une nouvelle instance de ResizeObserver, en lui passant une fonction de rappel qui sera exécutée chaque fois que la taille d'un élément surveillé change.
// Créer une nouvelle instance de ResizeObserver
const myObserver = new ResizeObserver(entries => {
// Ce callback sera exécuté lorsque la taille de l'élément observé change
for (let entry of entries) {
const targetElement = entry.target;
const newWidth = entry.contentRect.width;
const newHeight = entry.contentRect.height;
console.log(`L'élément ${targetElement.id || targetElement.tagName} a été redimensionné à ${newWidth}px x ${newHeight}px.`);
// Effectuer des actions en fonction de la nouvelle taille
}
});
Une fois que vous avez une instance d'observateur, vous pouvez lui indiquer quels éléments DOM observer en utilisant la méthode observe() :
// Obtenir l'élément que vous voulez observer
const myElement = document.getElementById('myResizableDiv');
// Commencer à observer l'élément
if (myElement) {
myObserver.observe(myElement);
console.log('Observation démarrée pour myResizableDiv.');
} else {
console.error('Élément #myResizableDiv non trouvé.');
}
Vous pouvez observer plusieurs éléments avec la même instance d'observateur :
const element1 = document.getElementById('chartContainer');
const element2 = document.querySelector('.responsive-sidebar');
if (element1) myObserver.observe(element1);
if (element2) myObserver.observe(element2);
Pour arrêter d'observer un élément spécifique, utilisez unobserve() :
// Arrêter d'observer un seul élément
if (myElement) {
myObserver.unobserve(myElement);
console.log('Observation arrêtée pour myResizableDiv.');
}
Pour arrêter d'observer tous les éléments et déconnecter complètement l'observateur, utilisez disconnect() :
// Déconnecter l'observateur de tous les éléments observés
myObserver.disconnect();
console.log('ResizeObserver déconnecté.');
La fonction de rappel et ResizeObserverEntry
La fonction de rappel passée à ResizeObserver reçoit un tableau d'objets ResizeObserverEntry. Chaque entrée correspond à un élément dont la taille a changé depuis la dernière notification.
Un objet ResizeObserverEntry fournit des informations cruciales sur le changement de taille :
target: Une référence à l'élément DOM qui a été redimensionné.contentRect: Un objetDOMRectReadOnlyqui représente la taille de la boîte de contenu de l'élément (la zone à l'intérieur du padding et de la bordure). C'est souvent la propriété la plus utilisée pour le dimensionnement général du contenu.borderBoxSize: Un tableau d'objetsResizeObserverSize. Cela fournit les dimensions de la boîte de bordure de l'élément, y compris le padding et la bordure. Utile lorsque vous devez en tenir compte dans vos calculs de mise en page. Chaque objet du tableau contientinlineSizeetblockSize.contentBoxSize: Un tableau d'objetsResizeObserverSize, similaire àborderBoxSizemais représentant la boîte de contenu. Ceci est considéré comme plus moderne et précis quecontentRectpour les dimensions du contenu, en particulier dans les mises en page multi-colonnes ou lors de l'utilisation de modes d'écriture.devicePixelContentBoxSize: Un tableau d'objetsResizeObserverSizefournissant les dimensions de la boîte de contenu en pixels de l'appareil, utile pour un rendu au pixel près, en particulier sur les écrans à haute résolution (high-DPI).
Regardons un exemple utilisant ces propriétés :
const detailedObserver = new ResizeObserver(entries => {
for (let entry of entries) {
console.log(`--- Élément redimensionné : ${entry.target.id || entry.target.tagName} ---`);
// contentRect (legacy) (DOMRectReadOnly)
console.log('ContentRect (legacy):');
console.log(` Largeur : ${entry.contentRect.width}px`);
console.log(` Hauteur : ${entry.contentRect.height}px`);
console.log(` X : ${entry.contentRect.x}px`);
console.log(` Y : ${entry.contentRect.y}px`);
// contentBoxSize moderne (tableau de ResizeObserverSize)
if (entry.contentBoxSize && entry.contentBoxSize.length > 0) {
const contentBox = entry.contentBoxSize[0];
console.log('ContentBoxSize (moderne):');
console.log(` Taille en ligne (largeur) : ${contentBox.inlineSize}px`);
console.log(` Taille de bloc (hauteur) : ${contentBox.blockSize}px`);
}
// BorderBoxSize (tableau de ResizeObserverSize)
if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {
const borderBox = entry.borderBoxSize[0];
console.log('BorderBoxSize:');
console.log(` Taille en ligne (largeur incluant padding/bordure) : ${borderBox.inlineSize}px`);
console.log(` Taille de bloc (hauteur incluant padding/bordure) : ${borderBox.blockSize}px`);
}
// DevicePixelContentBoxSize (tableau de ResizeObserverSize)
if (entry.devicePixelContentBoxSize && entry.devicePixelContentBoxSize.length > 0) {
const devicePixelBox = entry.devicePixelContentBoxSize[0];
console.log('DevicePixelContentBoxSize:');
console.log(` Taille en ligne (pixels de l'appareil) : ${devicePixelBox.inlineSize}px`);
console.log(` Taille de bloc (pixels de l'appareil) : ${devicePixelBox.blockSize}px`);
}
}
});
const observeMe = document.getElementById('observeThisDiv');
if (observeMe) {
detailedObserver.observe(observeMe);
}
Une note sur contentRect vs. contentBoxSize : Bien que contentRect soit largement pris en charge et intuitif, contentBoxSize et borderBoxSize sont des ajouts plus récents à la spécification. Ils fournissent un tableau d'objets ResizeObserverSize car un élément peut avoir plusieurs fragments s'il est dans une mise en page multi-colonnes. Pour la plupart des scénarios courants avec un seul fragment, vous accéderez au premier élément du tableau (par exemple, entry.contentBoxSize[0].inlineSize).
Cas d'utilisation concrets pour la gestion de mises en page responsives
Les applications de ResizeObserver sont incroyablement diverses, permettant aux développeurs de construire des interfaces utilisateur plus flexibles et résilientes. Voici quelques scénarios concrets convaincants :
Graphiques dynamiques et visualisations de données
Les bibliothèques de graphiques (comme Chart.js, D3.js, Highcharts, etc.) ont souvent besoin de redessiner ou d'ajuster leurs échelles lorsque leur conteneur change de taille. Traditionnellement, cela impliquait d'écouter window.resize puis de vérifier manuellement si le parent du graphique avait changé. Avec ResizeObserver, les graphiques peuvent simplement observer leur propre conteneur et répondre directement.
Exemple : Un tableau de bord avec plusieurs graphiques disposés dans une grille. Lorsqu'un utilisateur redimensionne un panneau ou change la mise en page, chaque graphique se redessine automatiquement pour s'adapter parfaitement à ses nouvelles dimensions, sans scintillement ni intervention manuelle.
Systèmes de grille et tableaux adaptatifs
Les tableaux responsifs sont notoirement délicats. Vous pourriez vouloir masquer certaines colonnes, convertir un tableau en une structure de type liste, ou ajuster la largeur des colonnes en fonction de l'espace disponible. Au lieu de s'appuyer sur des media queries qui s'appliquent à l'ensemble du viewport, ResizeObserver permet à un composant de tableau de décider de sa propre responsivité en fonction de sa propre largeur.
Exemple : Un tableau de liste de produits e-commerce. Lorsque son conteneur devient étroit, des colonnes spécifiques comme "ID produit" ou "niveau de stock" peuvent être masquées, et les colonnes restantes pourraient s'étendre pour remplir l'espace. Si le conteneur devient très étroit, le tableau pourrait même se transformer en une mise en page à base de cartes.
Composants d'interface utilisateur et widgets personnalisés
De nombreuses applications web comportent des composants d'interface utilisateur complexes et réutilisables : barres latérales, modales, panneaux déplaçables ou widgets intégrés. Ces composants doivent souvent adapter leur mise en page interne en fonction de l'espace qui leur est alloué par leur parent. ResizeObserver rend ce comportement auto-adaptatif simple.
Exemple : Un composant d'éditeur de texte riche personnalisé. Il pourrait afficher une barre d'outils complète lorsqu'il dispose d'un grand espace horizontal, mais passer automatiquement à un menu contextuel plus compact pour les options de formatage lorsque son conteneur se rétrécit. Un autre exemple est un lecteur multimédia personnalisé qui ajuste la taille et la position de ses commandes en fonction de la taille du conteneur de la vidéo.
Typographie responsive et mise à l'échelle des images
Au-delà des simples ajustements basés sur le viewport, ResizeObserver peut permettre une typographie et une gestion d'images véritablement fluides. Vous pouvez ajuster dynamiquement la taille des polices, la hauteur des lignes ou les sources d'images (par exemple, charger une image de plus haute résolution pour les grands conteneurs) en fonction de la taille réelle du bloc de texte ou du conteneur d'image, plutôt que seulement de la fenêtre.
Exemple : La zone de contenu principal d'un article de blog. La taille de la police des titres et des paragraphes pourrait subtilement augmenter ou diminuer pour optimiser la lisibilité dans la largeur spécifique de la colonne de contenu, indépendamment de la barre latérale ou du pied de page.
Intégrations tierces et Iframes
Les iframes sont notoirement difficiles à rendre responsives, surtout lorsque leur contenu doit communiquer sa hauteur souhaitée à la page parente. Bien que postMessage puisse être utilisé, c'est souvent fastidieux. Pour des scénarios plus simples où le parent de l'iframe doit réagir aux changements de taille externes de l'iframe (par exemple, si l'iframe a une hauteur dynamique basée sur son contenu interne), ResizeObserver peut notifier le conteneur parent.
Exemple : Intégration d'un formulaire tiers ou d'un outil de sondage. Si le formulaire développe ou réduit dynamiquement des sections, son <div> conteneur sur votre page peut écouter ces changements de taille via ResizeObserver et ajuster son propre style ou son comportement de défilement en conséquence.
Comportement de type "Container Query" aujourd'hui
Avant que les Container Queries CSS natives ne soient largement prises en charge, ResizeObserver était le principal moyen de parvenir à une logique similaire en JavaScript. Les développeurs pouvaient observer la taille d'un élément puis appliquer programmatiquement des classes CSS ou modifier des styles en fonction des seuils de largeur ou de hauteur de cet élément.
Exemple : Un composant de carte de produit. Si sa largeur est inférieure à 300px, il peut empiler son image et son texte verticalement. Si sa largeur est comprise entre 300px et 600px, il peut les placer côte à côte. Au-dessus de 600px, il pourrait afficher plus de détails. ResizeObserver fournit le déclencheur pour ces applications de style conditionnelles.
ResizeObserver vs. Autres techniques d'observation du DOM
Comprendre où ResizeObserver s'intègre dans l'écosystème des API DOM est crucial. Il complète, plutôt qu'il ne remplace, d'autres techniques d'observation.
window.resize : Toujours pertinent pour les mises en page globales
Comme discuté, window.resize est utile pour les changements qui affectent l'ensemble du viewport, comme la réorganisation des blocs de mise en page principaux (par exemple, déplacer une barre latérale vers le bas sur mobile). Cependant, il est inefficace et insuffisant pour les ajustements au niveau des composants. Utilisez window.resize lorsque vous devez réagir à la taille globale de la fenêtre du navigateur ; utilisez ResizeObserver pour les dimensions spécifiques des éléments.
MutationObserver : Pour les changements de structure et d'attributs du DOM
MutationObserver est conçu pour observer les changements dans l'arborescence DOM elle-même, tels que les ajouts/suppressions de nœuds, les changements de contenu textuel ou les modifications d'attributs. Il ne signale pas directement les changements de taille des éléments. Bien qu'un changement dans la structure du DOM puisse indirectement provoquer le redimensionnement d'un élément, MutationObserver ne vous indiquerait pas directement les nouvelles dimensions ; vous devriez les calculer vous-même après la mutation. Pour un suivi explicite de la taille, ResizeObserver est l'outil approprié.
Sondage (setInterval) : Un anti-modèle pour le suivi de la taille
Avant ResizeObserver, une méthode courante mais inefficace consistait à vérifier de manière répétée l'offsetWidth ou l'offsetHeight d'un élément à l'aide de setInterval. C'est généralement un anti-modèle car :
- Il consomme inutilement des cycles CPU, même lorsqu'aucun redimensionnement n'a eu lieu.
- L'intervalle de sondage est un compromis : trop fréquent, et c'est un gouffre de performances ; trop rare, et l'interface utilisateur réagit lentement.
- Il ne tire pas parti du pipeline de rendu optimisé du navigateur pour les changements de mise en page.
ResizeObserver offre une alternative déclarative, performante et optimisée par le navigateur.
element.getBoundingClientRect() / element.offsetWidth : Mesures statiques
Des méthodes comme getBoundingClientRect(), offsetWidth et offsetHeight fournissent des mesures immédiates et statiques de la taille et de la position d'un élément au moment où elles sont appelées. Elles sont utiles pour des mesures ponctuelles mais n'offrent aucune réactivité. Vous devriez les appeler à plusieurs reprises (par exemple, à l'intérieur d'un gestionnaire window.resize ou d'une boucle de sondage) pour détecter les changements, ce qui nous ramène aux inefficacités que ResizeObserver résout.
Meilleures pratiques et considérations avancées
Bien que puissant, l'utilisation efficace de ResizeObserver nécessite une compréhension de ses nuances et des pièges potentiels.
Éviter le ResizeObserverLoopError
Une erreur courante lors de la première utilisation de ResizeObserver est de modifier directement les propriétés de mise en page (par exemple, largeur, hauteur, padding, marges) d'un élément observé dans sa propre fonction de rappel. Cela peut entraîner une boucle infinie : un redimensionnement est détecté, le rappel modifie la taille de l'élément, ce qui déclenche un autre redimensionnement, et ainsi de suite. Le navigateur lèvera finalement une ResizeObserverLoopError pour empêcher la page de devenir non réactive.
Solution : Différer les changements de mise en page avec requestAnimationFrame.
Pour modifier en toute sécurité la mise en page de l'élément observé, différez ces changements à la prochaine frame d'animation. Cela permet au navigateur de terminer le passage de mise en page actuel avant que vous n'introduisiez de nouveaux changements susceptibles de déclencher un autre redimensionnement.
const saferObserver = new ResizeObserver(entries => {
for (let entry of entries) {
// S'assurer que nous ne modifions pas directement la taille de l'élément observé ici
// Si nous devons le faire, nous devons le différer.
const target = entry.target;
const newWidth = entry.contentRect.width;
// Exemple : Si nous ajustions la taille de la police de la cible en fonction de sa largeur
// MAUVAIS : target.style.fontSize = `${newWidth / 20}px`; // Pourrait causer une boucle
// BON : Différer le changement de style
requestAnimationFrame(() => {
// N'appliquer les changements que si l'élément est toujours connecté au DOM
// (important si les éléments peuvent être supprimés pendant une frame d'animation)
if (document.body.contains(target)) {
target.style.fontSize = `${newWidth / 20}px`;
console.log(`Taille de police ajustée pour ${target.id || target.tagName} à ${target.style.fontSize}.`);
}
});
}
});
const fontResizer = document.getElementById('fontResizerDiv');
if (fontResizer) {
saferObserver.observe(fontResizer);
}
Il est important de noter que cette erreur se produit généralement lorsque vous modifiez l'élément observé lui-même. Modifier un élément enfant ou un élément non lié dans le rappel est généralement sûr, car cela ne déclenchera pas un nouvel événement de redimensionnement sur l'élément observé à l'origine.
Implications sur les performances
ResizeObserver est conçu pour être très performant. Le navigateur regroupe les notifications de redimensionnement, ce qui signifie que le rappel n'est invoqué qu'une seule fois par frame, même si plusieurs éléments observés changent de taille ou si un seul élément change de taille plusieurs fois dans cette frame. Ce throttling intégré empêche les exécutions excessives de rappels.
Cependant, vous devez toujours être attentif au travail effectué à l'intérieur de votre rappel :
- Calculs coûteux : Évitez les manipulations lourdes du DOM ou les calculs complexes dans le rappel s'ils ne sont pas strictement nécessaires.
- Nombreux observateurs : Bien qu'efficace, l'observation d'un très grand nombre d'éléments (par exemple, des centaines ou des milliers) peut toujours avoir une surcharge de performance, surtout si chaque rappel effectue un travail important.
- Sorties anticipées : Si un changement de taille ne justifie pas une action, ajoutez une condition de sortie anticipée dans votre rappel.
Pour les actions qui sont coûteuses en calcul et qui n'ont pas besoin de se produire à chaque événement de redimensionnement (par exemple, les requêtes réseau, les redessinages complexes), envisagez de débouncer ou de throttler les actions déclenchées par le rappel de ResizeObserver, plutôt que le rappel lui-même. Cependant, pour la plupart des mises à jour de l'interface utilisateur, le throttling intégré est suffisant.
Considérations sur l'accessibilité
Lors de la mise en œuvre de mises en page dynamiques avec ResizeObserver, tenez toujours compte de l'impact sur l'accessibilité. Assurez-vous que les changements de mise en page :
- Sont prévisibles : Évitez les changements de contenu soudains et désorientants sans initiation de l'utilisateur ou contexte clair.
- Maintiennent la lisibilité : Le texte doit rester lisible et les éléments interactifs doivent rester accessibles, quelle que soit la taille du conteneur.
- Supportent la navigation au clavier : Les changements responsifs ne doivent pas casser l'ordre de focus du clavier ni rendre les éléments inaccessibles.
- Fournissent des alternatives : Pour les informations ou fonctionnalités critiques, assurez-vous qu'il existe d'autres moyens d'y accéder si le redimensionnement dynamique les rend cachées ou moins proéminentes.
Support des navigateurs et Polyfills
ResizeObserver bénéficie d'un excellent support sur tous les navigateurs modernes, y compris Chrome, Firefox, Edge, Safari et Opera. Cela en fait un choix fiable pour le développement web contemporain.
Pour les projets nécessitant une compatibilité avec les navigateurs plus anciens (par exemple, Internet Explorer), un polyfill peut être utilisé. Des bibliothèques comme resize-observer-polyfill peuvent fournir la fonctionnalité nécessaire, vous permettant d'utiliser l'API de manière cohérente dans un plus large éventail d'environnements.
Vous pouvez vérifier le dernier état de compatibilité sur Can I use... ResizeObserver.
Travailler avec les mises en page CSS (Flexbox, Grid, calc())
ResizeObserver fonctionne de manière transparente avec les techniques de mise en page CSS modernes comme Flexbox et Grid. Lorsque la taille d'un élément change en raison des règles de mise en page flex ou grid de son parent, ResizeObserver déclenchera correctement son rappel. Cette intégration est puissante :
- CSS gère la logique de mise en page principale (par exemple, les éléments se répartissant l'espace).
- JavaScript (via ResizeObserver) gère les ajustements secondaires, spécifiques au contenu, que CSS seul ne peut pas gérer (par exemple, redessiner un graphique, ajuster dynamiquement la taille des pistes de barres de défilement personnalisées).
De même, les éléments dont les tailles sont définies à l'aide de fonctions CSS comme calc() ou d'unités relatives (em, rem, vw, vh, %) déclencheront également ResizeObserver lorsque leurs dimensions en pixels calculées changeront. Cela garantit que l'API est réactive à pratiquement tout mécanisme qui affecte la taille rendue d'un élément.
Un exemple pas à pas : Créer une zone de texte auto-redimensionnable
Passons en revue un exemple pratique : une zone de texte qui ajuste automatiquement sa hauteur pour s'adapter à son contenu, puis réagit si son conteneur parent est redimensionné.
L'objectif est de créer un <textarea> qui s'agrandit verticalement à mesure que plus de contenu est tapé, mais qui garantit également que son <div> conteneur peut influencer sa hauteur maximale disponible si le conteneur lui-même change de taille.
Structure HTML
Nous allons mettre en place une structure HTML simple avec un conteneur parent et une zone de texte à l'intérieur.
<div class="container" id="textContainer">
<h3>Zone de contenu redimensionnable</h3>
<p>Tapez ici et regardez la zone de texte s'ajuster.</p>
<textarea id="autoResizeTextarea" placeholder="Commencez à taper..."></textarea>
<div class="resize-handle"></div>
</div>
Style CSS
Un peu de CSS pour le rendre visuellement clair et permettre au conteneur d'être redimensionné manuellement (pour la démonstration).
.container {
width: 100%;
max-width: 600px;
min-width: 300px;
min-height: 200px;
background-color: #f0f0f0;
border: 1px solid #ccc;
padding: 15px;
margin: 20px auto;
position: relative;
/* Permettre le redimensionnement manuel pour la démo */
resize: both;
overflow: auto;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
border-radius: 8px;
}
.container h3 {
color: #333;
margin-top: 0;
margin-bottom: 10px;
}
.container p {
color: #666;
font-size: 0.9em;
margin-bottom: 15px;
}
#autoResizeTextarea {
width: 100%;
min-height: 50px;
box-sizing: border-box; /* Inclure padding/bordure dans la largeur/hauteur */
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1em;
line-height: 1.5;
font-family: sans-serif;
overflow-y: hidden; /* Cacher la barre de défilement, nous gérerons la hauteur */
resize: none; /* Désactiver la poignée de redimensionnement par défaut du textarea */
}
Implémentation JavaScript
Maintenant, ajoutons le JavaScript pour rendre la zone de texte dynamiquement redimensionnable.
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.getElementById('autoResizeTextarea');
const container = document.getElementById('textContainer');
if (!textarea || !container) {
console.error('Éléments requis non trouvés. Vérifiez les ID HTML.');
return;
}
// Fonction pour ajuster la hauteur du textarea en fonction du contenu
const adjustTextareaHeight = () => {
// Réinitialiser la hauteur pour calculer la scrollHeight avec précision
textarea.style.height = 'auto';
// Définir la hauteur à scrollHeight, en s'assurant qu'elle s'adapte au contenu
textarea.style.height = `${textarea.scrollHeight}px`;
// OPTIONNEL : Contraindre la hauteur du textarea à la hauteur du contenu de son conteneur parent
// Cela empêche le textarea de dépasser la zone visible du conteneur.
const containerContentHeight = container.clientHeight -
(parseFloat(getComputedStyle(container).paddingTop) || 0) -
(parseFloat(getComputedStyle(container).paddingBottom) || 0);
const currentTextareaHeight = textarea.scrollHeight;
const spaceAboveTextarea = textarea.offsetTop - container.offsetTop;
const maxHeightAllowed = containerContentHeight - spaceAboveTextarea;
if (currentTextareaHeight > maxHeightAllowed && maxHeightAllowed > 0) {
textarea.style.height = `${maxHeightAllowed}px`;
textarea.style.overflowY = 'auto'; // Réactiver le défilement si contraint
} else {
textarea.style.overflowY = 'hidden'; // Cacher le défilement si le contenu rentre
}
};
// 1. Écouter les événements d'entrée sur le textarea pour ajuster la hauteur pendant que l'utilisateur tape
textarea.addEventListener('input', adjustTextareaHeight);
// 2. Utiliser ResizeObserver pour réagir aux changements de taille du conteneur
const containerResizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target === container) {
console.log(`Conteneur redimensionné à : ${entry.contentRect.width}px x ${entry.contentRect.height}px`);
// Lorsque le conteneur est redimensionné, nous devons réévaluer la hauteur du textarea
// surtout s'il était contraint par la hauteur du parent.
// Différer cela pour éviter ResizeObserverLoopError si les enfants du conteneur affectent sa taille.
requestAnimationFrame(() => {
if (document.body.contains(container)) {
adjustTextareaHeight();
}
});
}
}
});
// Commencer à observer le conteneur
containerResizeObserver.observe(container);
// Ajustement initial lors du chargement de la page
adjustTextareaHeight();
});
Dans cet exemple :
- Nous avons un
textareaqui augmente sa hauteur en fonction de sascrollHeightlorsque l'utilisateur tape. - Un
ResizeObserverest attaché au conteneur parent (#textContainer). - Lorsque le conteneur est redimensionné manuellement (en utilisant la propriété CSS
resize: both;), le rappel de l'observateur se déclenche. - À l'intérieur du rappel, nous réexécutons
adjustTextareaHeight(). Cela garantit que si le conteneur rétrécit, la contrainte de hauteur du textarea est réévaluée, activant potentiellement sa barre de défilement si le contenu ne rentre plus. - Nous utilisons
requestAnimationFramepour l'appel àadjustTextareaHeight()dans le rappel de l'observateur pour éviter un potentielResizeObserverLoopError, surtout si la taille du textarea influençait d'une manière ou d'une autre la taille du conteneur dans une mise en page plus complexe.
Cela démontre comment ResizeObserver permet à un composant (le textarea) d'être véritablement responsif non seulement à son propre contenu, mais aussi à l'espace dynamique fourni par son parent, créant une expérience fluide et conviviale.
L'avenir : ResizeObserver et les Container Queries natives
Avec l'avènement des Container Queries CSS natives (par exemple, les règles @container), qui gagnent un large soutien des navigateurs, une question fréquente se pose : ResizeObserver a-t-il encore un rôle à jouer ?
La réponse est un oui retentissant.
- Container Queries : Se concentrent principalement sur le style piloté par CSS en fonction de la taille d'un conteneur parent. Elles vous permettent d'appliquer des styles (comme changer
display,font-size,grid-template-columns) directement dans les règles CSS sans JavaScript. C'est idéal pour la responsivité purement présentationnelle et liée à la mise en page. - ResizeObserver : Excelle lorsque vous avez besoin de JavaScript pour réagir aux changements de taille. Cela inclut :
- Redessiner programmatiquement un canvas (par exemple, des graphiques, des jeux).
- Ajuster une logique d'interface utilisateur complexe pilotée par JavaScript (par exemple, réinitialiser une bibliothèque tierce, calculer de nouvelles positions pour des éléments déplaçables).
- Interagir avec d'autres API JavaScript en fonction de la taille (par exemple, charger dynamiquement différentes tailles d'images, contrôler la lecture vidéo).
- Lorsque vous avez besoin de dimensions précises en pixels pour des calculs que CSS seul ne peut pas fournir ou effectuer efficacement.
En substance, les Container Queries gèrent le style déclaratif, tandis que ResizeObserver gère la logique impérative et programmatique. Ce sont des outils complémentaires qui, ensemble, créent la boîte à outils ultime pour des applications web véritablement responsives.
Conclusion
L'API ResizeObserver est un outil indispensable pour les développeurs web modernes qui s'efforcent de créer des interfaces utilisateur vraiment dynamiques et responsives. En fournissant un mécanisme efficace et événementiel pour observer les changements de taille de n'importe quel élément DOM, elle nous fait dépasser les limites de la responsivité centrée sur le viewport pour entrer dans le domaine de l'adaptabilité robuste au niveau du composant.
De l'ajustement transparent des visualisations de données à leurs conteneurs à la création de composants d'interface utilisateur conscients d'eux-mêmes, ResizeObserver vous permet de créer des expériences web plus résilientes, performantes et conviviales. Adoptez cette API puissante pour élever votre développement front-end, concevoir des mises en page qui s'adaptent avec grâce à chaque contexte, et livrer des produits numériques exceptionnels à un public mondial.
Commencez à intégrer ResizeObserver dans vos projets dès aujourd'hui et débloquez un nouveau niveau de contrôle sur vos designs web responsifs !